#pragma once
#include "mq_channel.hpp"

namespace mq
{
    class Connection
    {
    private:
        muduo::net::TcpConnectionPtr _conn;
        ProtobufCodecPtr _codec;
        VirtualHost::ptr _host;
        ConsumerManager::ptr _cmp;
        ThreadPool::ptr _pool;
        ChannelManager::ptr _channels;
    private:
       void basicResponse(bool ok, const std::string &rid, const std::string &cid)
        {
            basicCommonResponse resp;
            resp.set_rid(rid);
            resp.set_cid(cid);
            resp.set_ok(ok);
            _codec->send(_conn, resp);
        }
    public:
    using ptr = std::shared_ptr<Connection>;
        Connection(const VirtualHost::ptr &host,
                const ConsumerManager::ptr &cmp,
                const ProtobufCodecPtr &codec,
                const muduo::net::TcpConnectionPtr &conn,
                const ThreadPool::ptr &pool):
                _conn(conn),_codec(codec),_host(host),_cmp(cmp),_pool(pool),
                _channels(std::make_shared<ChannelManager>()){}

        void openChannel(const openChannelRequestPtr &req){
            bool ret = _channels->openChannel(req->cid(),_host,_cmp,_codec,_conn,_pool);
            if(ret == false)
            {
                return basicResponse(false,req->rid(),req->cid());
            }
            return basicResponse(true,req->rid(),req->cid());
        }
        void closeChannel(const closeChannelRequestPtr &req){
            _channels->closeChannel(req->cid());
            return basicResponse(true, req->rid(), req->cid());
        }
        Channel::ptr getChannel(const std::string &cid){
            return _channels->getChannel(cid);
        }
    };

    class ConnectionManager
    {
    private:
        std::mutex _mutex;
        std::unordered_map<muduo::net::TcpConnectionPtr,Connection::ptr> _conns;
    public:
    using ptr = std::shared_ptr<ConnectionManager>;
        ConnectionManager(){}
        void newConnection(const VirtualHost::ptr &host,
                    const ConsumerManager::ptr &cmp,
                    const ProtobufCodecPtr &codec,
                    const muduo::net::TcpConnectionPtr &conn,
                    const ThreadPool::ptr &pool){
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _conns.find(conn);
            if (it != _conns.end()) {
                return ;
            }
            Connection::ptr self_conn = std::make_shared<Connection>(host,cmp, codec, conn, pool);
            _conns.insert(std::make_pair(conn, self_conn));   
        }
        void delConnection(const muduo::net::TcpConnectionPtr &conn){
            std::unique_lock<std::mutex> lock(_mutex);
            _conns.erase(conn);
        }
        Connection::ptr getConnection(const muduo::net::TcpConnectionPtr &conn){
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _conns.find(conn);
            if (it == _conns.end()) {
                return Connection::ptr();
            }
            return it->second;
        }
    };
}
